/*******************************************************

	A simple link list implementation
	Course	: CSC3150
	Author	: Tsang Hing Chung
	Date	: 29 September, 2009
	Version	: 1.0


*********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// for pid_t type
#include <sys/types.h>
#include <unistd.h>

#include "linkedList.h"

// uncomment the following line if you don't want any debug message
//#define NDEBUG

int listLength(struct jobNode *headNode){
	int i;
	struct jobNode *ptr;
	for (i = 0, ptr = headNode; ptr ; ptr = ptr->next, i++);
	return i;
}

struct jobNode *createNode(int arrivalTime, char *cmd, int duration){
	struct jobNode *newNode = NULL;
	newNode = malloc(sizeof(struct jobNode));
	if (!newNode){
#ifndef NDEBUG
		fprintf(stderr, "Error(%s:%d): cannot malloc()\n", __FILE__, __LINE__);
#endif
	}
	else{
		// set everything in newNode to zero
		memset(newNode, 0, sizeof(struct jobNode));
		newNode->arrivalTime = arrivalTime;
		newNode->duration = duration;
		if (cmd){
			// copy at most 255 characters
			strncpy(newNode->command, cmd, MAX_CMD_SIZE - 1);
			// make sure the array is null-terminated
			newNode->command[MAX_CMD_SIZE - 1] = '\0';
		}
		newNode->next = NULL;
		// initialize other field that you need
		newNode->ready_next = NULL;
		newNode->child_pid = 0;
		if (duration == -1){
		    newNode->quantum = 2;
		    newNode->runinfinity = 1;
			newNode->timeRemain = -1;
			newNode->ptimeRemain = -1;
        }else{
            newNode->runinfinity = 0;
            newNode->timeRemain = duration;
            newNode->ptimeRemain = duration;
        }
	}
	return newNode;
}

void enqueue(struct jobNode **ptrHeadNode, struct jobNode *newNode){
	if (!ptrHeadNode){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : ptrHeadNode == NULL!\n", __FILE__, __LINE__);
#endif
		return;
	}

	if (!(*ptrHeadNode) && !newNode){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : *ptrHeadNode and newNode are both NULL!\n", __FILE__, __LINE__);
#endif
		return;
	}

	if (!newNode){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : newNode is NULL! List remain unchange\n", __FILE__, __LINE__);
#endif
		return;
	}

	if (!(*ptrHeadNode)){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : *ptrHeadNode == NULL, "
			"the new list will equal newNode\n", __FILE__, __LINE__);
#endif
		*ptrHeadNode = newNode;
	}
	else if (*ptrHeadNode == newNode){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : *ptrHeadNode == newNode! Nothing enqueued\n",
			__FILE__, __LINE__);
#endif
	}
	else{
		struct jobNode *ptr;
		// finding the tail
		for (ptr = *ptrHeadNode; ptr ; ptr = ptr->next){
			if (ptr == newNode){
#ifndef NDEBUG
				fprintf(stderr, "Warning(%s:%d) : newNode is already in the list!\n",
					__FILE__, __LINE__);
#endif
				return;
			}
			if (ptr->next == NULL){
				ptr->next = newNode;
				break;
			}
		}
	}
}

struct jobNode *dequeue(struct jobNode **ptrHeadNode){
	if (!ptrHeadNode){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : ptrHeadNode == NULL!\n", __FILE__, __LINE__);
#endif
		return NULL;
	}
	if (!(*ptrHeadNode)){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : ptrHeadNode points to NULL!\n", __FILE__, __LINE__);
#endif
		return NULL;
	}

	// save the first node
	struct jobNode *retNode = *ptrHeadNode;
	// Make the second node the new head of the list
	*ptrHeadNode = retNode->next;
	// remove the linkage of the old head node
	retNode->next = NULL;

	return retNode;
}

struct jobNode *removeNodeFromList(struct jobNode **ptrHeadNode, struct jobNode *target){
	if (!ptrHeadNode){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : ptrHeadNode == NULL!\n", __FILE__, __LINE__);
#endif
		return NULL;
	}
	if (!(*ptrHeadNode)){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : ptrHeadNode points to NULL!\n", __FILE__, __LINE__);
#endif
		return NULL;
	}
	if (!target){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : target == NULL!\n", __FILE__, __LINE__);
#endif
		return NULL;
	}

	// if target is the first node, just dequeue it
	if (target == *ptrHeadNode){
		return dequeue(ptrHeadNode);
	}

	struct jobNode *ptr;
	struct jobNode *retNode = NULL;
	for (ptr = *ptrHeadNode; ptr ; ptr = ptr->next){
		if (ptr->next == target){
			ptr->next = target->next;
			target->next = NULL;
			retNode = target;
			break;
		}
	}
	return retNode;
}

void freeList(struct jobNode **ptrHeadNode){
	if (!ptrHeadNode){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : ptrHeadNode == NULL!\n", __FILE__, __LINE__);
#endif
		return ;
	}
	if (!(*ptrHeadNode)){
#ifndef NDEBUG
		fprintf(stderr, "Warning(%s:%d) : Cannot free an empty list!\n", __FILE__, __LINE__);
#endif
		return ;
	}

	struct jobNode *ptr = NULL;
	while (*ptrHeadNode){
		ptr = dequeue(ptrHeadNode);
		if (ptr){
			// free other malloc'd memory of the node, if any
			free(ptr);
			ptr = NULL;
		}
	}
}

// Reference: Here is Dr. Wong's implementation
void printList(struct jobNode *headNode){
	if (!headNode){
		printf("An empty list!\n");
		return ;
	}
	struct jobNode *ptr;
	char 	hline[STRING_BUFFER_MAX],
			space[STRING_BUFFER_MAX],
			separ[STRING_BUFFER_MAX],
			title[STRING_BUFFER_MAX];

	int i = 0, len, max_len = 0, count = 0;

	for (ptr = headNode ; ptr ; ptr = ptr->next){
		len = strlen(ptr->command);
		if(len > max_len)
			max_len = len;
	}

	sprintf(title, " ID | START | CMD");
	sprintf(hline, "----+-------+----");

	space[0] = '\0';
	separ[0] = '\0';
	if(max_len > 3)
	{
		for(i = 0; i < (max_len - 3); i++)	// 3 is length of 'CMD'
			sprintf(space, "%s ", space);
		for(i = 0; i < (max_len - 3); i++)	// 3 is length of 'CMD'
			sprintf(separ, "%s-", separ);
	}
	sprintf(title, "%s%s | Duration  \n", title, space);
	sprintf(hline, "%s%s-+---------\n", hline, separ);

	//////////////////////////////////////////////////

	fprintf(stdout, "%s", title);
	for (ptr = headNode ; ptr ; ptr = ptr->next){
		fprintf(stdout, "%s", hline);
		fprintf(stdout, " %-2d | %-5d | %s", ++count, ptr->arrivalTime, ptr->command);
		len = strlen(ptr->command);

		space[0] = '\0';
		for(i = 0; i < (max_len - len); i++)
			sprintf(space, "%s ", space);

		fprintf(stdout, "%s | ", space);
		if(ptr->duration == -1)
			fprintf(stdout, "  INF  ");
		else
			fprintf(stdout, "%-3d sec", ptr->duration);
		fprintf(stdout, "\n");
	}
	fprintf(stdout, "\n");
}
